<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>演示爬虫</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <base target="_blank">
</head>
<body>
<div id="app">
    <el-container>
        <el-header class="flex">
            <el-input v-model="params.url" style="flex: auto;margin-right: 10px"
                      placeholder="URL 地址或 HTML 代码"></el-input>
            <el-input v-model="params.table" style="width:200px;margin-right: 10px" placeholder="保存数据的表名"></el-input>
            <el-button :disabled="params.fields.filter(f=>!f.value).length>0" @click="capture" type="primary">采集数据
            </el-button>
            <el-button :disabled="!data" @click="capture(true)" type="primary">导出数据</el-button>
            <el-button @click="saveRecord()" type="primary" plain>保存进度</el-button>
        </el-header>
        <el-main>
            <el-tabs v-model="activeName">
                <template v-if="records.length>0">
                    <el-tab-pane label="历史记录" name="0">
                        <el-table :data="records" stripe style="width: 100%">
                            <el-table-column type="index" align="center"></el-table-column>
                            <el-table-column prop="date" label="时间" width="120" align="center"></el-table-column>
                            <el-table-column prop="name" label="表名" width="150"></el-table-column>
                            <el-table-column label="列数" width="100">
                                <span slot-scope="scope" :title="scope.row.data.fields.map(f=>f.name).join(',')">
                                    {{scope.row.data.fields.length}}
                                </span>
                            </el-table-column>
                            <el-table-column label="初始地址">
                                <template slot-scope="scope">{{scope.row.data.url}}</template>
                            </el-table-column>
                            <el-table-column label="采集地址">
                                <template slot-scope="scope">{{scope.row.data.urlRegex}}</template>
                            </el-table-column>
                            <el-table-column label="操作" align="center" width="200">
                                <template slot-scope="scope">
                                    <el-button @click="setRecord(scope.row)" type="primary" plain size="small">加载</el-button>
                                    <el-button @click="delRecord(scope.row)" type="primary" plain size="small">移除</el-button>
                                </template>
                            </el-table-column>
                        </el-table>
                    </el-tab-pane>
                </template>
                <el-tab-pane label="页面" name="1">
                    <div v-if="/^\s*<.+/.test(params.url)" class="full" v-html="params.url"
                         style="overflow: auto"></div>
                    <iframe v-else class="full" :src="params.url"></iframe>
                </el-tab-pane>
                <template v-if="params.url">
                    <el-tab-pane label="采集字段" name="2">
                        <el-form ref="form" label-width="200px" style="width:calc(100% - 180px);margin: auto">
                            <el-form-item>
                                <template #label>
                                    <div style="text-align: left; padding-left: 16px">捕获URL</div>
                                </template>
                                <div class="flex">
                                    <el-input v-model="params.urlRegex" style="margin-right: 10px"></el-input>
                                    <el-button @click="addField">添加新字段</el-button>
                                </div>
                            </el-form-item>
                            <el-form-item v-for="f,i in params.fields">
                                <template #label>
                                    <div class="flex">
                                        <el-input v-model.trim="f.name" style="margin-right: 10px"></el-input>
                                        <el-checkbox v-model="f.must">必选</el-checkbox>
                                    </div>
                                </template>
                                <div class="flex">
                                    <el-input v-model="f.value"></el-input>
                                    <el-button v-if="params.fields.length>1" @click="params.fields.splice(i,1)"
                                               style="margin-left: 10px">移除
                                    </el-button>
                                </div>
                            </el-form-item>
                        </el-form>
                    </el-tab-pane>
                </template>
                <template v-if="data">
                    <el-tab-pane :label="'数据('+(data?data.length:'无')+')'" name="3">
                        <el-table :data="data" stripe style="width: 100%">
                            <el-table-column type="index"></el-table-column>
                            <el-table-column v-for="f in params.fields" :label="f.name">
                                <template slot-scope="scope">
                                    <span :title="scope.row[f.name]">
                                    {{scope.row[f.name].substring(0,20) + (scope.row[f.name].length>20?'...':'')}}
                                    </span>
                                </template>
                            </el-table-column>
                            <el-table-column prop="url" label="地址">
                                <template slot-scope="scope">
                                    <el-link :href="scope.row.url">{{scope.row.url}}</el-link>
                                </template>
                            </el-table-column>
                        </el-table>
                    </el-tab-pane>
                </template>
            </el-tabs>
        </el-main>
    </el-container>
    <el-dialog title="采集进度" :visible.sync="capturing" :close-on-click-modal="false">
        <p>采集: {{progress.url}}</p>
        <el-progress :percentage="progress.rate"></el-progress>
        <span slot="footer">
            <el-button @click="stop(true)">停 止</el-button>
        </span>
    </el-dialog>
</div>
<script>
    var v = new Vue({
        el: "#app",
        data: {
            activeName: "1",
            params: {
                url: "",
                urlRegex: "",
                fields: [{name: "字段1", value: "", must: true}]
            },
            data: null,
            capturing: false,
            progress: {},
            records: []
        },
        created() {
            axios.get("stop");
            let fieldsJson = localStorage.getItem("crawlParams");
            if (fieldsJson) {
                this.params = JSON.parse(fieldsJson);
            }
            this.loadRecord();
        },
        methods: {
            addField() {
                this.params.fields.push({
                    name: `字段${this.params.fields.length + 1}`,
                    value: this.params.fields[this.params.fields.length - 1].value,
                    must: true
                });
            },
            capture(down) {
                let usp = new URLSearchParams();
                usp.append("url", this.params.url);
                this.params.fields.filter(f => f.value).forEach(f => {
                    usp.append(f.name + (f.must ? '*' : ''), f.value);
                });
                if (this.params.urlRegex) {
                    usp.append("urls", this.params.urlRegex);
                }
                if (this.params.table) {
                    usp.append("table", this.params.table);
                }
                localStorage.setItem("crawlParams", JSON.stringify(this.params));
                let url = "capture";
                if (down == true) {
                    usp.append("down", "true");
                    url += "?" + usp.toString();
                    open(url, "_blank");
                } else {
                    axios.post(url, usp).then(res => {
                        if (this.capturing == true) {
                            this.capturing = false;
                            this.data = res.data;
                            if (this.data && this.data.length > 0) {
                                this.activeName = "3";
                            }
                        }
                    });
                    this.stop();
                    this.capturing = true;
                    this.showProgress();
                }
            },
            showProgress() {
                axios.get("progress").then(res => {
                    this.progress = res.data;
                    if (this.capturing) {
                        setTimeout(() => {
                            this.showProgress();
                        }, 500);
                    }
                });
            },
            stop(ajax) {
                this.capturing = false;
                this.progress = {rate: 0}
                if (ajax == true) {
                    axios.get("stop");
                }
            },
            backup() {
                return false;
            },
            saveRecord() {
                localStorage.setItem("crawlParams" + new Date().getTime(), JSON.stringify(this.params));
                this.loadRecord();
                this.$message("保存成功");
            },
            loadRecord() {
                this.records = [];
                for (let i = 0; i < localStorage.length; i++) {
                    let key = localStorage.key(i);
                    if (/crawlParams\d+/.test(key)) {
                        let data = JSON.parse(localStorage.getItem(key));
                        let date = new Date(parseInt(key.replace(/crawlParams(\d+)/, "$1")));
                        this.records.push({
                            key: key,
                            name: data.table,
                            date: date.toLocaleString().substring(5),
                            time: date.getTime(),
                            data: data
                        })
                    }
                }
                if (this.records.length) {
                    this.records.sort((a, b) => b.time - a.time);
                }
            },
            delRecord(row) {
                this.$confirm("请确认是否要移除该记录?").then(() => {
                    localStorage.removeItem(row.key);
                    this.records = this.records.filter(o => o.key != row.key);
                });
            },
            setRecord(row) {
                this.params = row.data;
            }
        }
    })
</script>
</body>
</html>

<style>
    html, body, #app, .el-container, .el-tabs, .el-tab-pane {
        padding: 0px;
        margin: 0px;
        height: 100%;
    }

    .el-tab-pane {
        overflow: hidden;
    }

    .full {
        padding: 0px;
        margin: 0px;
        width: 100%;
        height: 100%;
        overflow: auto;
        border: 0px;
    }

    #app > .el-container > .el-main {
        padding: 0px 20px;
    }

    .el-tabs__content {
        height: calc(100% - 55px);
    }

    .flex {
        display: flex;
        justify-content: space-between;
        align-items: center;
    }
</style>